package com.feng.security.mac;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.lenovo.lps.log.Log;

public class CryptTool {
	
	private static Log log = Log.getInstance(CryptTool.class);
	
	public static final byte[] HEX_CHAR_TABLE = { (byte) '0', (byte) '1',
			(byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
			(byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B',
			(byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' };

	public static String toHexString(byte[] raw)
			throws UnsupportedEncodingException {
		byte[] hex = new byte[2 * raw.length];
		int index = 0;

		for (byte b : raw) {
			int v = b & 0xFF;
			hex[index++] = HEX_CHAR_TABLE[v >>> 4];
			hex[index++] = HEX_CHAR_TABLE[v & 0xF];
		}
		return new String(hex, "ASCII");
	}
	
	public static int hexChar2int(byte hexChar){
		if( hexChar<HEX_CHAR_TABLE[10] ){
			return hexChar-HEX_CHAR_TABLE[0];
		}else{
			return hexChar-HEX_CHAR_TABLE[10]+10;
		}
	}
	public static byte[] hexString2Byte(String s){
		byte[] raw =  s.getBytes();
		byte[] hex = new byte[ raw.length/2];
		
		for (int i=0;i<hex.length;i++) {
			int leftBit4 = hexChar2int(raw[2*i]) << 4;
			hex[i] = (byte)( leftBit4 | hexChar2int(raw[2*i+1]) );
		}
		return hex;
	}
		

	private static final String SHA1_PRNG = "SHA1PRNG";		
	// Default here is 128-bits of random goodness.	
	//private static final int DEFAULT_RANDOM_SIZE = 128;		
		
	// This isn't thread safe but we probably don't really care	
	// since all we're doing is reading a bunch of random numbers	
	// out of the generator.	
	private static final SecureRandom sRandom__;	
	static {	    
		try {	    	
			sRandom__ = SecureRandom.getInstance( SHA1_PRNG );	
			} catch ( NoSuchAlgorithmException e ) {	        
				throw new Error(e);	    
				}	
			}	
	/**	 * Get the number of next random bits in this SecureRandom	 
	 * * generators' sequence.	 
	 * * @param size how many random bits you want	 
	 * * @return	 
	 *  */	
	public static byte [] getNextSecureRandom ( int bytecount )
	{
		
		// Usually 64-bits of randomness, 8 bytes	    
		final byte [] bytes = new byte[ bytecount ];	    	   
		// Get the next 64 random bits. Forces SecureRandom	   
		// to seed itself before returning the bytes.	    
		sRandom__.nextBytes(bytes);	    	   
		return bytes;			
	} 
	
	/*
	 * // This method returns the available implementations for a service type
	 * public static String[] getCryptoImpls(String serviceType) { Set<String>
	 * result = new HashSet<String>();
	 * 
	 * Provider[] providers = Security.getProviders(); for ( int i = 0; i <
	 * providers.length; i++ ) { // Get services provided by each provider Set
	 * keys = providers[i].keySet(); for ( Iterator it = keys.iterator();
	 * it.hasNext(); ) { String key = ( String ) it.next(); key = key.split( " "
	 * )[0];
	 * 
	 * if ( key.startsWith( serviceType + "." ) ) { result.add( key.substring(
	 * serviceType.length() + 1 ) ); } else if ( key.startsWith( "Alg.Alias." +
	 * serviceType + "." ) ) { // This is an alias result.add( key.substring(
	 * serviceType.length() + 11 ) ); } } }
	 * 
	 * return result.toArray( new String[result.size()] );
	 * 
	 * } public static String[] getServiceTypes() {
	 * 
	 * Set result = new HashSet();
	 * 
	 * // All providers Provider[] providers = Security.getProviders(); for (int
	 * i=0; i<providers.length; i++) {
	 * 
	 * // Get services provided by each provider Set keys =
	 * providers[i].keySet();
	 * 
	 * for (Iterator it=keys.iterator(); it.hasNext(); ) {
	 * 
	 * String key = (String)it.next(); key = key.split(" ")[0];
	 * 
	 * if (key.startsWith("Alg.Alias")) { // Strip the alias key =
	 * key.substring(10); }
	 * 
	 * int ix = key.indexOf('.'); result.add(key.substring(0,ix)); }
	 * 
	 * }
	 * 
	 * return (String[])result.toArray(new String[result.size()]);
	 * 
	 * }
	 * 
	 * public static void listServiceTypes(){ String[] names =
	 * getServiceTypes();
	 * 
	 * for(int i =0;i<names.length;i++){
	 * System.out.println(names[i]+"--------------"); listProvider(names[i]); }
	 * }
	 * 
	 * public static void listProvider(String service){ String[] names =
	 * getCryptoImpls(service);
	 * 
	 * for(int i =0;i<names.length;i++){ System.out.println(names[i]); }
	 * 
	 * }
	 * 
	 * public static void printKeys(String alg){ String keys; SecretKey key;
	 * byte[] ksba; String[] names = getCryptoImpls(alg);
	 * System.out.println("Begin CryptoImpls:"+alg+"------------------------");
	 * for(int i =0;i<names.length;i++){ try { key=createKey(names[i]); ksba=
	 * key.getEncoded(); keys= toHexString(ksba);
	 * System.out.print("alg="+names[i]+"&");
	 * System.out.println("keysize[byte]="
	 * +keys.length()/2+"&key[Hexstring]="+keys); } catch (Exception e) { //
	 * TODO Auto-generated catch block //e.printStackTrace(); //
	 * System.out.print("alg:"+names[i]+"::"); //System.out.println("error"); }
	 * } System.out.println("End CryptoImpls:"+alg+"------------------------");
	 * 
	 * }
	 */

	public static void main(String[] args){
		
		byte[] bb = hexString2Byte("B125A533104656CCE8F6B4714231A3C2");
	
		byte[] bData = new byte[]{ (byte)33 };
		String s = ">";
		try{
			String sHex  = toHexString(s.getBytes());
			String Hex33 = toHexString(bData);
			System.out.println("test hex : " + s + " --hex--> " + sHex + " -unhex-> " + new String(hexString2Byte(sHex)) );
			System.out.println("test hex : " + 33 + " --hex--> " + Hex33 + " -unhex-> " + new String(hexString2Byte(Hex33))  );
		}catch(Exception  ee ){
			ee.printStackTrace();
		}
		for( int i=0;i<32;i++ ){
			SecretKey key = createHMACKey();
			String kv = getKeyValue( key );
			System.out.println("HMACKey:" + kv  );
			//SecretKey key2 = retireHMACKey(kv);
			if(!kv.equals( getKeyValue(retrieveHMACKey(kv)))){
				System.out.println("wrong !"  );
			}
		}
		for( int i=0;i<8;i++ ){
			SecretKey key = createAESKey();
			String kv = getKeyValue( key );
			System.out.println("AESKey:" + kv  );
			//SecretKey key2 = retireHMACKey(kv);
			if(!kv.equals( getKeyValue(retrieveAESKey(kv)))){
				System.out.println("wrong !"  );
			}
			
		}
	}

	public static SecretKey createKey(String algrithm)
			throws NoSuchAlgorithmException {
		KeyGenerator keyGen = null;
		SecretKey key = null;
		
		keyGen = KeyGenerator.getInstance(algrithm);
		
		if (keyGen != null){
			key = keyGen.generateKey();
		}
		return key;
	}

	public static byte[] HMACSHA256_128(Key keySpec, byte[] src) {
		return HMACSHA256_128(keySpec, src, 16);
	}
	public static byte[] HMACSHA256_128(Key keySpec, byte[] src,int nLength) {
		javax.crypto.Mac mac = null;
		// SecretKey key=new SceretKey();
		byte[] res = null;

		try {
			mac = javax.crypto.Mac.getInstance("HmacSHA256");
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			mac.init(keySpec);
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// get 32 bytes HMACSHA256 result.
		res = mac.doFinal(src);

		// now truncate into nLength bytes HMACSHA256-128 result.
		byte[] ret = new byte[nLength];
		for (int i = 0; i < nLength; i++)
			ret[i] = res[i];
		return ret;

	}

	public static byte[] encryptAES(Key keySpec, byte[] src) {
		javax.crypto.Cipher cip = null;
		// SecretKey key=new SceretKey();
		byte[] res = null;

		try {
			try {
				cip = javax.crypto.Cipher.getInstance("AES");
				cip.init(javax.crypto.Cipher.ENCRYPT_MODE, keySpec);
				res = cip.doFinal(src);
			} catch (NoSuchPaddingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return res;
	}

	public static byte[] decryptAES(Key keySpec, byte[] src) {
		javax.crypto.Cipher cip = null;
		// SecretKey key=new SceretKey();
		byte[] res = null;

		try {
			try {
				cip = javax.crypto.Cipher.getInstance("AES");
				cip.init(javax.crypto.Cipher.DECRYPT_MODE, keySpec);
				res = cip.doFinal(src);
			} catch (NoSuchPaddingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (BadPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return res;
	}
	
	public static SecretKey createHMACKey(){
		try {
			return CryptTool.createKey("HmacSHA256");
		}catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static String getKeyValue( SecretKey keya ){
		String keys=null;
		try {
			byte[] ksba= keya.getEncoded();
			keys= CryptTool.toHexString(ksba);

			return keys;
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return keys;
	}
	
	public static SecretKey createAESKey(){
		try {
			return CryptTool.createKey("AES");
		}catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public static SecretKey retrieveHMACKey(String keyValue){
		try {
			SecretKey keya = new SecretKeySpec(hexString2Byte(keyValue),"HmacSHA256");
			//get secret key.
			return keya ;
		} catch (IllegalArgumentException e) {
			log.error("retrieveHMACKey error, keyValue: " + keyValue, e );
			if( log.isDebugEnabled() ){
				e.printStackTrace();
			}
			return null;
		}
	}
	
	public static SecretKey retrieveAESKey(String keyValue){
	
		try {
			SecretKey keya = new SecretKeySpec(hexString2Byte(keyValue),"AES");
			//get secret key.
			return keya ;
		} catch (IllegalArgumentException e) {
			log.error("retrieveAESKey error, keyValue: " + keyValue, e );
			if( log.isDebugEnabled() ){
				e.printStackTrace();
			}
			return null;
		}
	}
	
	public static SecretKey retrieveKey(String algrithm,String keyValue){
		
		try {
			SecretKey keya = new SecretKeySpec(hexString2Byte(keyValue),algrithm);
			//get secret key.
			return keya ;
		} catch (IllegalArgumentException e) {
			log.error("retrieveAESKey error, keyValue: " + keyValue, e );
			if( log.isDebugEnabled() ){
				e.printStackTrace();
			}
			return null;
		}
	}
	
}
